#include <netlib/net/ping.h>

using namespace netlib::net;

Ping::Ping( WORD size ) : m_size ( size ), m_pipe( NULL ), m_icmpInstance( NULL )
{
	WSADATA wsaData;
	int result = WSAStartup( 0x202, &wsaData );

	if ( !result )
	{
		m_icmpInstance = LoadLibrary("ICMP.DLL");

		m_pipe = ( IP_ECHO_REPLY *)GlobalAlloc( GMEM_FIXED | GMEM_ZEROINIT, sizeof( IP_ECHO_REPLY ) + size );
	}
}

Ping::~Ping()
{
	if ( m_pipe ) GlobalFree( m_pipe );
	if ( m_icmpInstance ) FreeLibrary( m_icmpInstance );
	WSACleanup();
}

int Ping::ping( const char *ip, int timeout )
{
	if ( !m_icmpInstance ) return ( ERROR_PING_NOT_INSTANCE );
	if ( !m_pipe ) return ( ERROR_PING_NOT_ENOUGHT_MEMORY );

	if ( timeout > 10000 ) timeout = 10000; 

	struct hostent *hostEnt = gethostbyname( ip );
	if ( !hostEnt ) return ( ERROR_PING_NO_EXIST_HOST );


	typedef HANDLE ( WINAPI* fncCreateFile )( void );
	typedef BOOL ( WINAPI* fncCloseHandle )( HANDLE );
	typedef DWORD ( WINAPI* fncSendEcho )( HANDLE, DWORD, LPVOID, WORD, IP_OPTION_INFORMATION_PING *, LPVOID, DWORD, DWORD );

	fncCreateFile IcmpCreateFile;
	fncCloseHandle IcmpCloseHandle;
	fncSendEcho IcmpSendEcho;

	IcmpCreateFile = ( fncCreateFile )GetProcAddress( m_icmpInstance, "IcmpCreateFile" );
	IcmpCloseHandle = ( fncCloseHandle )GetProcAddress( m_icmpInstance, "IcmpCloseHandle" );
	IcmpSendEcho = ( fncSendEcho )GetProcAddress( m_icmpInstance, "IcmpSendEcho" );
    
	if ( ( !IcmpCreateFile ) || ( !IcmpCloseHandle ) || ( !IcmpSendEcho ) ) return ( ERROR_PING_NOT_DLL_LOAD );

	HANDLE handleIP = IcmpCreateFile();
	if ( handleIP == INVALID_HANDLE_VALUE ) return ( ERROR_PING_ICMP_CREATE_FAILURE );

	memset( m_ackPingBuffer, '\xAA', m_size );

	m_pipe->data = m_ackPingBuffer;
	m_pipe->dataSize = m_size;

	DWORD status = IcmpSendEcho( handleIP, *( ( DWORD * )hostEnt->h_addr_list[ 0 ] ), m_ackPingBuffer, 	m_size, NULL, m_pipe, sizeof( IP_ECHO_REPLY ) + m_size, timeout);

	if ( status )
	{
    	return ( int( m_pipe->roundTripTime ) );
    }
	return ( ERROR_PING_ICMP_SEND_ERROR );
}